﻿using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Xml.Linq;
using GalaSoft.MvvmLight.Messaging;
using Microsoft.Office.Interop.Word;
using Office = Microsoft.Office.Core;

namespace Songhay.Office2010.WordWalkingStick
{
    using Properties;
    using Songhay.Composition;
    using Songhay.Composition.Extensions;
    using Songhay.Models;
    using Songhay.Office2010.Word;
    using Songhay.Wpf.Mvvm.Models;
    using Songhay.Wpf.Mvvm.ViewModels;
    using Songhay.Wpf.Mvvm.Views;
    using Songhay.Wpf.WordWalkingStick;
    using Songhay.Wpf.WordWalkingStick.Views;

    /// <summary>
    /// Defines the the Ribbon (XML) item.
    /// </summary>
    /// <remarks>
    /// TODO:  Follow these steps to enable the Ribbon (XML) item:
    /// 1. <see cref="ThisAddIn.CreateRibbonExtensibilityObject"/>
    /// 
    /// 2. Create callback methods in the "Ribbon Callbacks" region of this class to handle user
    ///    actions, such as clicking a button. Note: if you have exported this Ribbon from the Ribbon designer,
    ///    move your code from the event handlers to the callback methods and modify the code to work with the
    ///    Ribbon extensibility (RibbonX) programming model.
    /// 
    /// 3. Assign attributes to the control tags in the Ribbon XML file to identify the appropriate callback methods in your code.
    /// 
    /// For more information, see the Ribbon XML documentation in the Visual Studio Tools for Office Help.
    /// </remarks>
    [ComVisible(true)]
    public class WordRibbon : Office.IRibbonExtensibility
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="WordRibbon"/> class.
        /// </summary>
        /// <remarks>
        /// See “Visual Studio 2010: Ribbon XML”
        /// (http://msdn.microsoft.com/en-us/library/aa942866.aspx)
        /// </remarks>
        public WordRibbon()
        {
            //Receive MvvmLight message:
            Messenger.Default.Register(this,
                new Action<GenericMessage<WordWalkingStickCompositionHost>>(message =>
            {
                var host = message.Content;
                this._messaging = host.CompositionMessaging;
                this._commands = host.WordCommands;
                this._wordApplication = host.WordApplication;
            }));
        }

        #region IRibbonExtensibility Members

        /// <summary>
        /// Gets the custom UI.
        /// </summary>
        /// <param name="ribbonID">The ribbon ID.</param>
        public string GetCustomUI(string ribbonID)
        {
            //TODO: change call for Ribbon XML to external Commands assembly.
            var xml = GetResourceText("Songhay.Office2010.WordWalkingStick.WordRibbon.xml");
            var xd = XDocument.Parse(xml);
            this._xRibbonTabWordWalkingStick = xd
                .Root.Element(customui + "ribbon")
                .Element(customui + "tabs")
                .Elements(customui + "tab")
                .Where(e => (string)e.Attribute("id") == "TabWordWalkingStick")
                .First();
            return xml;
        }

        #endregion

        #region Ribbon

        /// <summary>
        /// Binds the control.
        /// </summary>
        /// <param name="control">The control.</param>
        public object BindControl(Office.IRibbonControl control)
        {
            var message = new RibbonControlMessage
            {
                ControlId = control.Id,
                ControlTag = control.Tag
            };
#if DEBUG
            System.Diagnostics.Debug.WriteLine(
                "Processing RibbonControlMessage (ID: {0}, Tag: {1})",
                    message.ControlId, message.ControlTag);
#endif
            var command = GetCommand(message);
            if(command != null)
            {
                return command.ExecuteCommandForBinding(this._wordApplication, message);
            }

            return null;
        }

        /// <summary>
        /// Gets the image.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <remarks>
        /// Reference: “Using Custom Images in Ribbon UI Solutions”
        /// http://xldennis.wordpress.com/2009/03/02/using-custom-images-in-ribbon-ui-solutions/
        /// </remarks>
        public Bitmap GetImage(Office.IRibbonControl control)
        {
            switch(control.Id)
            {
                case "AboutWordWalkingStick":
                    return new Bitmap(Resources.DarkAkyinkyin64);
                case "InsertContentControlAmazonImage":
                    return new Bitmap(Resources.AmazonIcon64);
            }

            return null;
        }

        /// <summary>
        /// Invokes the command for controls that can be clicked.
        /// </summary>
        /// <param name="control">The control.</param>
        public void InvokeCommand(Office.IRibbonControl control)
        {
            var message = new RibbonControlMessage
            {
                ControlId = control.Id,
                ControlTag = control.Tag,
                ControlValue = this.GetControlValue(control.Id)
            };
#if DEBUG
            System.Diagnostics.Debug.WriteLine(
                "Processing RibbonControlMessage (ID: {0}, Tag: {1})",
                    message.ControlId, message.ControlTag);
#endif
            var command = GetCommand(message);
            if(command != null)
            {
                command.ExecuteCommand(this._wordApplication, message);
            }
            else
            {
                this._messaging.SendMessage(message);
            }
        }

        /// <summary>
        /// Invokes the command for controls that can be clicked.
        /// </summary>
        /// <param name="control">The control.</param>
        public void InvokeCommandAndDisplay(Office.IRibbonControl control)
        {
#if DEBUG
            System.Diagnostics.Debug.WriteLine(
                "Processing RibbonControlMessage (ID: {0}, Tag: {1})",
                    control.Id, control.Tag);
#endif
            var processRibbonControlMessage = new Action<IControlInteractionMessage>(
                message =>
                {
                    var command = GetCommand(message);
                    if(command != null)
                    {
                        command.ExecuteCommand(this._wordApplication, message);
                    }
                    else
                    {
                        this._messaging.SendMessage(message);
                    }
                }
            );

            switch(control.Id)
            {
                case "AboutWordWalkingStick":
                    var about = new AboutView(
                        new AboutViewModel(
                            this.GetType().Assembly));
                    about.ShowDialog();
                    break;

                default:
                    var message = new ControlInteractionMessage<ClientView>
                    {
                        ControlId = control.Id,
                        ControlTag = control.Tag,
                        SpecifiedView = new ClientView()
                    };

                    processRibbonControlMessage(message);

                    break;
            }
        }

        /// <summary>
        /// Invokes the command for controls that can be pressed.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="isPressed">if set to <c>true</c> control is pressed.</param>
        public void InvokeCommandForPressed(Office.IRibbonControl control, bool isPressed)
        {
            var message = new RibbonControlMessage
            {
                ControlValue = isPressed,
                ControlId = control.Id,
                ControlTag = control.Tag
            };
#if DEBUG
            System.Diagnostics.Debug.WriteLine(
                "Processing RibbonControlMessage (ID: {0}, Tag: {1})",
                    message.ControlId, message.ControlTag);
#endif
            var command = GetCommand(message);
            if(command != null)
            {
                command.ExecuteCommand(this._wordApplication, message);
            }
            else
            {
                this._messaging.SendMessage(message);
            }
        }

        /// <summary>
        /// Determines whether the specified control is enabled only when a Document is open.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <returns>
        /// 	<c>true</c> if Document is open; otherwise, <c>false</c>.
        /// </returns>
        public bool IsControlEnabledForDocument(Office.IRibbonControl control)
        {
            return (this._wordApplication.Windows.Count > 0);
        }

        /// <summary>
        /// Conventional handler for when the OfficeRibbon is loaded
        /// into the Microsoft Office application.
        /// </summary>
        /// <param name="ribbonUI">The ribbon UI.</param>
        public void Ribbon_Load(Office.IRibbonUI ribbonUI)
        {
            this._ribbon = ribbonUI;
        }

        #endregion

        XNamespace customui = "http://schemas.microsoft.com/office/2009/07/customui";

        string GetControlValue(string controlId, string controlTag = "button")
        {
            var control = this._xRibbonTabWordWalkingStick
                .Descendants(customui + controlTag)
                .Where(e => (string)e.Attribute("id") == controlId)
                .First();

            var description = (string)control.Attribute("description");
            var screentip = (string)control.Attribute("screentip");

            return string.IsNullOrEmpty(description) ? screentip : description;
        }

        static string GetResourceText(string resourceName)
        {
            Assembly asm = Assembly.GetExecutingAssembly();
            string[] resourceNames = asm.GetManifestResourceNames();
            for(int i = 0; i < resourceNames.Length; ++i)
            {
                if(string.Compare(resourceName, resourceNames[i], StringComparison.OrdinalIgnoreCase) == 0)
                {
                    using(StreamReader resourceReader = new StreamReader(asm.GetManifestResourceStream(resourceNames[i])))
                    {
                        if(resourceReader != null)
                        {
                            return resourceReader.ReadToEnd();
                        }
                    }
                }
            }
            return null;
        }

        IOfficeWordCommand GetCommand(IControlInteractionMessage message)
        {
            var item = this._commands
                .Where(i => i.Name == message.ControlId || i.Name == message.ControlTag)
                .FirstOrDefault();
            return item;
        }

        ICompositionMessaging _messaging;
        Office.IRibbonUI _ribbon;
        OfficeWordCommands _commands;
        Application _wordApplication;
        XElement _xRibbonTabWordWalkingStick;
    }
}
